package com.example.demo.config;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;

@Aspect
@Order(0)
@Component
public class DataSourceAspect {

    private static final Logger LOG = LoggerFactory.getLogger(DataSourceAspect.class);

    @Pointcut("within(@org.springframework.stereotype.Service *)")
    public void chooseDataSource(){
    }

    @Around("chooseDataSource()")
    public Object chooseDs(ProceedingJoinPoint pjp) throws Throwable{
        DataSourceType dsType = DataSourceManager.getType();

        if(dsType == null || dsType == DataSourceType.SLAVE){
            Method method = ((MethodSignature)pjp.getSignature()).getMethod();
            if(isWriteRequired(method)){
                DataSourceManager.setMaster();
            }else{
                DataSourceManager.setSlave();
            }
        }

        LOG.debug("choose datasource = {}", DataSourceManager.getType());

        return pjp.proceed();
    }

    private boolean isWriteRequired(Method method){
        Transactional transAnnotation = AnnotationUtils.findAnnotation(method, Transactional.class);
        if(transAnnotation != null){
            if(!transAnnotation.readOnly()){
                LOG.info("write db required");
                return true;
            }
        }
        return false;
    }
}